package pr.lanmu.service;

import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.update.UpdateChain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pr.lanmu.annotation.CustomLog;
import pr.lanmu.common.mapper.LowCodePermissionMapper;
import pr.lanmu.common.mapper.ModulePermissionMapper;
import pr.lanmu.common.mapper.PermissionMapper;
import pr.lanmu.common.mapper.PermissionMockRuleMapper;
import pr.lanmu.common.po.*;
import pr.lanmu.config.util.ConvertUtil;
import pr.lanmu.config.util.Log;
import pr.lanmu.config.util.Res;
import pr.lanmu.config.web.CustomException;
import pr.lanmu.controller.request.PermissionReq;
import pr.lanmu.controller.response.PermissionRes;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 权限服务类
 */
@Service
@CustomLog
@SuppressWarnings("all")
public class PermissionService {
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private LowCodePermissionMapper lowCodePermissionMapper;
    @Autowired
    private ModulePermissionMapper modulePermissionMapper;
    @Autowired
    private PermissionMockRuleMapper permissionMockRuleMapper;

    /**
     * 保存低代码接口
     *
     * @param param 保存接口请求参数
     * @return Res<?>
     */
    @Transactional(rollbackFor = Exception.class)
    public Res<?> save(PermissionReq.save param) {
        return Res.success(() -> {
            //保存接口信息
            Permission permission = ConvertUtil.copy(param, new Permission());
            permission.setIsLowCode(true);
            try {
                permissionMapper.insertOrUpdate(permission, true);
            } catch (DuplicateKeyException e) {
                Log.warn("接口url重复", e);
                throw new CustomException("接口url已存在");
            }
            //保存低代码信息
            LowCodePermission lowCodePermission = ConvertUtil.copy(param, new LowCodePermission());
            //移除低代码信息数据
            new LowCodePermission().where(LowCodePermission::getPermissionId).eq(permission.getId()).remove();
            //保存低代码信息
            lowCodePermission.setPermissionId(permission.getId());
            lowCodePermissionMapper.insertSelective(lowCodePermission);
        });
    }

    /**
     * 删除低代码接口
     *
     * @param param 删除接口请求参数
     * @return Res<?>
     */
    public Res<?> del(PermissionReq.del param) {
        return Res.success(() -> {
            // 判断是否为低代码接口
            QueryChain.of(Permission.class)
                    .where(Permission::getId).eq(param.getId())
                    .and(Permission::getIsLowCode).eq(true)
                    .oneOpt()
                    .ifPresentOrElse(permission -> {
                        // 判断是否存在子接口
                        QueryChain.of(Permission.class)
                                .where(Permission::getPid).eq(param.getId())
                                .limit(1)
                                .oneOpt()
                                .ifPresent(p -> {
                                    throw new CustomException("该接口下存在子接口，不可删除");
                                });
                    }, () -> {
                        throw new CustomException("编码接口不可删除");
                    });
            permissionMapper.deleteById(param.getId());
        });
    }

    /**
     * 获取所有接口树
     *
     * @param param 获取所有接口树请求参数
     * @return Res<List < PermissionRes.all>>
     */
    public Res<List<PermissionRes.all>> all(PermissionReq.all param) {
        return Res.success(() -> {
            QueryChain<Permission> qw = QueryChain.of(Permission.class);
            // 是否获取开放接口
            if (!param.getHasOpen()) qw.where(Permission::getOpen).ne(true);
            List<PermissionRes.all> list = qw.orderBy(Permission::getCreateTime).desc()
                    .orderBy(Permission::getName).asc()
                    .list()
                    .parallelStream()
                    .map(permission -> {
                        PermissionRes.all item = ConvertUtil.copy(permission, new PermissionRes.all());
                        // 获取低代码接口信息
                        if (permission.getIsLowCode()) {
                            QueryChain.of(LowCodePermission.class)
                                    .where(LowCodePermission::getPermissionId).eq(permission.getId())
                                    .oneOpt()
                                    .ifPresent(lowCodePermission -> ConvertUtil.copy(lowCodePermission, item));
                        }
                        return item;
                    })
                    .collect(Collectors.toList());
            // 查询低代码接口信息
            List<PermissionRes.all> tree = getTree(list, 0L);
            tree.sort(Comparator.comparing(PermissionRes.all::getCreateTime)
                    .reversed());
            return tree;
        });
    }

    /**
     * 递归获取接口树
     *
     * @param list 权限列表
     * @param pid  父权限id
     * @return List<PermissionRes.all>
     */
    private List<PermissionRes.all> getTree(List<? extends Permission> list, Long pid) {
        List<PermissionRes.all> root = new ArrayList<>();
        list.forEach(module -> {
            if (!Objects.equals(module.getPid(), pid)) return;
            PermissionRes.all item = ConvertUtil.copy(module, new PermissionRes.all());
            root.add(item);
            List<PermissionRes.all> tree = getTree(list, item.getId());
            item.setChildren(tree.isEmpty() ? null : tree);
        });
        return root;
    }

    /**
     * 获取所有接口
     *
     * @param param .
     * @return .
     */
    public Res<List<PermissionRes.list>> list(PermissionReq.list param) {
        return Res.success(QueryChain.of(Permission.class).select(Permission::getId, Permission::getUrl, Permission::getName).listAs(PermissionRes.list.class));
    }

    /**
     * 创建低代码接口并绑定模块
     *
     * @param param .
     * @return .
     */
    @Transactional(rollbackFor = Exception.class)
    public Res<?> saveAndBingModule(PermissionReq.saveAndBingModule param) {
        return Res.success(() -> {
            //保存接口信息
            Permission permission = ConvertUtil.copy(param, new Permission());
            permission.setIsLowCode(true);
            //保存低代码接口
            permissionMapper.insertSelective(permission);
            LowCodePermission lowCodePermission = ConvertUtil.copy(param, new LowCodePermission());
            //移除低代码信息数据
            UpdateChain.of(LowCodePermission.class).where(LowCodePermission::getPermissionId).eq(permission.getId()).remove();
            //保存低代码详细信息
            lowCodePermission.setPermissionId(permission.getId());
            lowCodePermissionMapper.insertSelective(lowCodePermission);
            //接口绑定模块
            ModulePermission modulePermission = new ModulePermission();
            modulePermission.setModuleId(param.getModuleId());
            modulePermission.setPermissionId(permission.getId());
            modulePermissionMapper.insertSelective(modulePermission);
        });
    }

    /**
     * 保存接口规则
     *
     * @param param .
     * @return .
     */
    public Res<?> saveRule(PermissionReq.saveRule param) {
        return Res.success(() -> {
            PermissionMockRule permissionMockRule = ConvertUtil.copy(param, new PermissionMockRule());
            Optional.ofNullable(permissionMockRule.oneById()).ifPresentOrElse(e -> permissionMockRule.updateById(), permissionMockRule::save);
        });
    }

    /**
     * 保存接口测试结果
     *
     * @param param .
     * @return .
     */
    public Res<?> saveTestResult(PermissionReq.saveTestResult param) {
        return Res.success(() -> {
            UpdateChain.of(Permission.class)
                    .set(Permission::getTestResult, param.getTestResult())
                    .set(Permission::getMockTime, System.currentTimeMillis())
                    .where(Permission::getId).eq(param.getId())
                    .update();
        });
    }

    /**
     * 设置是否开启自动化测试
     *
     * @param param .
     * @return .
     */
    public Res<?> setAutoTest(PermissionReq.setAutoTest param) {
        return Res.success(() -> {
            UpdateChain.of(Permission.class)
                    .set(Permission::getAutoTest, param.getAutoTest())
                    .where(Permission::getId).eq(param.getId())
                    .update();
        });
    }

    /**
     * 设置是否开放
     *
     * @param param .
     * @return .
     */
    public Res<?> setOpen(PermissionReq.setOpen param) {
        return Res.success(() -> {
            UpdateChain.of(Permission.class)
                    .set(Permission::getOpen, param.getOpen())
                    .where(Permission::getId).eq(param.getId())
                    .update();
        });
    }

    /**
     * 设置是否鉴权
     *
     * @param param .
     * @return .
     */
    public Res<?> setAuthentication(PermissionReq.setAuthentication param) {
        return Res.success(() -> {
            UpdateChain.of(Permission.class)
                    .set(Permission::getAuthentication, param.getAuthentication())
                    .where(Permission::getId).eq(param.getId())
                    .update();
        });
    }


    /**
     * 复制接口目录
     *
     * @param param .
     * @return .
     */
    @Transactional
    public Res<?> copyDir(PermissionReq.copyDir param) {
        return Res.success(() -> {
            //查询目录接口信息
            Permission permission = Optional.ofNullable(permissionMapper.selectOneById(param.getId()))
                    .orElseThrow(() -> new CustomException("接口不存在"));
            String dirUrl = permission.getUrl();
            //查询目录下的低代码接口信息
            List<Permission> permissionList = QueryChain.of(Permission.class)
                    .where(Permission::getPid).eq(param.getId())
                    .and(Permission::getIsLowCode).eq(true)
                    .list();
            //设置目录信息
            permission.setId(null);
            permission.setPid(permission.getPid());
            permission.setName(param.getName());
            permission.setUrl(param.getUrl());
            permission.setCreateUserId(null);
            permission.setCreateTime(null);
            permission.setUpdateTime(null);
            permission.setUpdateUserId(null);
            //保存目录信息
            permissionMapper.insertSelective(permission);
            //复制目录下的接口信息
            permissionList.forEach(item -> {
                Long id = item.getId();
                //保存接口信息
                item.setId(null);
                item.setPid(permission.getId());
                //设置测试结果为false
                item.setTestResult(false);
                item.setCreateUserId(null);
                item.setCreateTime(null);
                item.setUpdateTime(null);
                item.setUpdateUserId(null);
                String url = item.getUrl();
                if (url.startsWith(dirUrl)) item.setUrl(url.replace(dirUrl, param.getUrl()));
                else return;
                permissionMapper.insertSelective(item);

                //保存接口下的低代码信息
                QueryChain.of(LowCodePermission.class)
                        .where(LowCodePermission::getPermissionId).eq(id)
                        .oneOpt()
                        .ifPresent(e -> {
                            e.setPermissionId(item.getId());
                            e.setCreateTime(null);
                            e.setCreateUserId(null);
                            e.setUpdateTime(null);
                            e.setUpdateUserId(null);
                            lowCodePermissionMapper.insertSelective(e);
                        });
                //保存接口下的模拟数据信息
                QueryChain.of(PermissionMockRule.class)
                        .where(PermissionMockRule::getId).eq(id)
                        .list()
                        .forEach(e -> {
                            e.setId(item.getId());
                            e.setCreateTime(null);
                            e.setCreateUserId(null);
                            e.setUpdateTime(null);
                            e.setUpdateUserId(null);
                            permissionMockRuleMapper.insertSelective(e);
                        });
            });
        });
    }

    /**
     * 接口配置信息一键使用默认值
     *
     * @param param .
     * @return .
     */
    @Transactional
    public Res<?> config(PermissionReq.config param) {
        return Res.success(() -> {
            TableInfo tableInfo = new TableInfo().where(TableInfo::getTableName).eq(param.getTable()).oneOpt().orElseThrow(() -> new CustomException("未找到表信息"));
            List<String> info = param.getInfo();
            String type = param.getType();
            info.forEach(e -> {
                if (e.equals("0")) {
                    //入参
                    String paramInfo = tableInfo.getParamInfo();
                    JSON parse = JSONUtil.parse(paramInfo);
                    parse.putByPath("create_time", null);
                    parse.putByPath("update_time", null);
                    parse.putByPath("create_user_id", null);
                    parse.putByPath("update_user_id", null);
                    if (type.equals("0")) parse.putByPath("id", null);
                    paramInfo = JSONUtil.toJsonStr(parse);
                    UpdateChain.of(LowCodePermission.class)
                            .set(LowCodePermission::getParam, paramInfo)
                            .where(LowCodePermission::getPermissionId).eq(param.getId())
                            .update();
                } else if (e.equals("1")) {
                    //校验
                    String paramValid = tableInfo.getParamValid();
                    JSON parse = JSONUtil.parse(paramValid);
                    parse.putByPath("create_time", null);
                    parse.putByPath("update_time", null);
                    parse.putByPath("create_user_id", null);
                    parse.putByPath("update_user_id", null);
                    if (type.equals("0")) parse.putByPath("id", null);
                    paramValid = JSONUtil.toJsonStr(parse);
                    UpdateChain.of(LowCodePermission.class)
                            .set(LowCodePermission::getParamValid, paramValid)
                            .where(LowCodePermission::getPermissionId).eq(param.getId())
                            .update();
                } else if (e.equals("2")) {
                    //规则
                    String paramMock = tableInfo.getParamMock();
                    JSON parse = JSONUtil.parse(paramMock);
                    parse.putByPath("create_time", null);
                    parse.putByPath("update_time", null);
                    parse.putByPath("create_user_id", null);
                    parse.putByPath("update_user_id", null);
                    if (type.equals("0")) parse.putByPath("id", null);
                    paramMock = JSONUtil.toJsonStr(parse);
                    PermissionMockRule permissionMockRule = new PermissionMockRule().
                            setId(param.getId())
                            .setRule(paramMock);
                    permissionMockRule.oneByIdOpt().ifPresentOrElse(o -> permissionMockRule.updateById(), permissionMockRule::save);
                }
            });
        });
    }
}

